#ifndef __M_UTIL_H__
#define __M_UTIL_H__

#include <iostream>
#include <sys/stat.h>
#include <unistd.h>
#include <fstream>
#include <experimental/filesystem>
#include "../Referenced_Lib/bundle/bundle.h"
#include <json/json.h> //json/json.h代表/usr/include/json/json.h
// 原本是jsoncpp/json/json.h,但是在我测试的Linux操作系统里,是/usr/include/json/json.h,所以采用了这种头文件

/*
    1.提供获取文件大小的接口
    2.提供获取文件最近修改时间和访问时间的接口
    3.获取文件的大小
    4.获取文件名称
    5.判断文件是否存在
    6.在文件系统删除某个文件
    7.获取文件pos位置开始，长度为len的文件内容
    8.获取整个文件的内容
    9.往文件写入内容
    10.压缩文件
    11.解压文件
    12.创建目录
    13.获取该目录下的所有文件
    14.提供格式化数据
    15.提供解析数据
*/

namespace cloud
{
    namespace fs = std::experimental::filesystem; // c++17新增加的库
    class FileUtil
    {
    private:
        std::string _filename;

    public:
        FileUtil(const std::string filename)
            : _filename(filename)
        {
            
        }

        bool Remove() // 在文件系统里清除名字为_filename的文件
        {
            if (IsExist() == false) // 不存在
            {
                std::cout << _filename << "is not exist" << std::endl;
                return false;
            }
            remove(_filename.c_str());
            return true;
        }

        time_t FileLastATime() // 文件的atime,即最后访问时间
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cerr << "get file atime fail" << std::endl;
                return -1;
            }
            return st.st_atime;
        }

        time_t FileLastMTime() // 获取文件最后修改的时间
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cerr << "get file mtine fail" << std::endl;
                return -1;
            }
            return st.st_mtime;
        }

        int64_t FileSize() // 获取文件的大小
        {
            struct stat st;
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cerr << "get file size fail" << std::endl;
                return -1;
            }
            return st.st_size;
        }
        std::string FileName()
        {
            auto pos = _filename.find_last_of("/\\"); // ‘/’是linux下的文件分隔符,"\"是windows的文件分隔符,"\\"地提高\防止第二个被识别为其他
            if (pos == std::string::npos)
            {
                return _filename;
            }
            return _filename.substr(pos + 1); // 从'/'或者'\'开始切割
        }

        bool GetPosLen(std::string *str, size_t pos, size_t len) // 从pos位置开始获取长度为len的文件内容
        {
            size_t filesize = FileSize();
            if (pos + len > filesize) // 判断获取长度是否超过文件的大小
            {
                std::cerr << "get file len is error" << std::endl;
                return false;
            }
            std::ifstream ifs;
            ifs.open(_filename, std::ios::binary); // 打开文件
            if (ifs.is_open() == false)
            {
                std::cerr << "open file fail" << std::endl;
                return false;
            }
            ifs.seekg(pos, std::ios::beg); // 从文件开始,将文件指针移动pos位置
            str->resize(len);
            ifs.read(&(*str)[0], len); // 读取到str
            if (ifs.good() == false)
            {
                std::cerr << "read file fail" << std::endl;
                ifs.close();
                return false;
            }
            ifs.close();
            return true;
        }

        bool GetContent(std::string *str) // 获取整个文件的内容
        {
            size_t len = FileSize();
            return GetPosLen(str, 0, len);
        }

        bool SetContent(const std::string &str)
        {
            std::ofstream ofs;
            //文件不存在会创建
            ofs.open(_filename, std::ios::binary); // 打开文件
            if (ofs.is_open() == false)
            {
                std::cerr << "open file fail" << std::endl;
                return false;
            }
            ofs.write(&str[0], str.size()); // 往文件写入内容
            if (ofs.good() == false)
            {
                std::cerr << "write into file fail" << std::endl;
                ofs.close();
                return false;
            }
            ofs.close();
            return true;
        }

        bool compress(const std::string &packname) // 压缩文件
        {
            std::string str;
            if (GetContent(&str) == false) // 获取文件内容存到str里面
            {
                std::cerr << "compress get file content fail" << std::endl;
                return false;
            }
            std::string pack = bundle::pack(bundle::LZIP, str); // 压缩内容
            FileUtil file(packname);                            // 使用压缩文件名字创建该文件结构体
            if (file.SetContent(pack) == false)
            {
                std::cerr << "compress write file fail" << std::endl;
                return false;
            }
            return true;
        }

        bool uncompress(const std::string filename) // 解压文件
        {
            std::string str;
            if (GetContent(&str) == false) // 获取文件内容存到str里面
            {
                std::cerr << "uncompress get file content fail" << std::endl;
                return false;
            }
            std::string unpack = bundle::unpack(str); // 解压内容
            FileUtil file(filename);                  // 创建对应文件名的结构体,并存入内容
            if (file.SetContent(unpack) == false)
            {
                std::cerr << "uncompress writre file fail" << std::endl;
                return false;
            }
            return true;
        }

        bool IsExist() // 判断文件是否存在
        {
            return fs::exists(_filename);
        }

        bool CreatDictroy() // 创建目录
        {
            if (IsExist())
                return false;
            return fs::create_directories(_filename);
        }

        bool ScanDictory(std::vector<std::string> *arry) // 获取该目录下的所有文件
        {
            for (auto &p : fs::directory_iterator(_filename)) // 遍历该目录下的所有子目录和文件
            {
                if (fs::is_directory(p))
                    continue;                                          // 如果是目录,就跳过
                arry->push_back(fs::path(p).relative_path().string()); // 文件就进行存入
            }
            return true;
        }
    };

    class JsonUtil
    {
    public:
        static bool Serialize(Json::Value &root, std::string *str)
        {
            Json::StreamWriterBuilder swb;                                 // 建造者
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter()); // 开始建造
            std::stringstream ss;
            if (sw->write(root, &ss) != 0) // 写入
            {
                std::cerr << "json write fail" << std::endl;
                return false;
            }
            *str = ss.str();
            return true;
        }

        static bool UnSerialize(const std::string &str, Json::Value *root)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());
            std::string err;
            if (cr->parse(str.c_str(), str.c_str() + str.size(), root, &err) < 0)
            {
                std::cerr << "json write fail" << std::endl;
                return false;
            }
            return true;
        }
    };
}

#endif
